﻿// Web Worker for HTA

function deleteAllWorkers() {
	for (var i in window) {
		if (i.substr(0, 9) === "__worker_") {
			var w = window[i];
			if (w) {
				// オブジェクトがまだ有効な場合、終了処理を行う。
				w.finalize();
			}
		}
	}
}

function formatDate(d) {
return (d.getFullYear()+'-'+(d.getMonth()+1)+'-'+d.getDate()+'T'+d.getHours()+'-'
	+d.getMinutes()+'-'+d.getSeconds()).replace(/(\D)(\d)(\b|(?=\D))/g,"$10$2");
}

var Worker = function(scriptPath)
{
	this.scriptPath = scriptPath;
	this.wsh;
	this.wcExec;
	this.fso;
	this.tempFilePath = '';
	this.workerThreadScriptPath = '';
	this.workerName = "__worker_";
	this.readCharCount = 0;
	this.postMessage = function(param) {
		this._param = param;
		
		this.wsh = new ActiveXObject("WScript.Shell");
		{
			this.fso = new ActiveXObject("Scripting.FileSystemObject");
			var TemporaryFolder = 2;
			var tempDirPath = this.fso.GetSpecialFolder(TemporaryFolder);
			this.tempFilePath = tempDirPath + "\\" + "hta_worker_" + formatDate(new Date()) + "_" + this.fso.GetTempName();
			this.workerThreadScriptPath = tempDirPath + "\\" + "hta_worker_" + formatDate(new Date()) + "_" + this.fso.GetTempName() + ".wsf";
		}
		var workerThreadScriptPath = this
		{
			var curDirPath = this.fso.GetFolder(".").Path;
			var scriptFullPath = scriptPath;
			if (scriptFullPath.indexOf(/^https?:/) !== 0) {
				scriptFullPath = curDirPath + "\\" + scriptPath.replace("/", "\\");
			}
			var jsonlibPath = "json2.js";
			{
				var scripts = document.getElementsByTagName('script');
				for (var i = 0; i < scripts.length; i++) {
					if (scripts[i].src) {
						if (scripts[i].src.match(/\bjson2\.js$/i)) {
							jsonlibPath = scripts[i].src;
							break;
						}
					}
				}
			}
			jsonlibPath = document.location + "/../" + jsonlibPath;
			var wsf = this.fso.CreateTextFile(this.workerThreadScriptPath);
			wsf.WriteLine('<job id="WorkerJob">');
			wsf.WriteLine('<script language="JScript" src="' + jsonlibPath + '"/>');
			wsf.WriteLine('<script language="JScript" src="' + scriptFullPath + '"/>');
			wsf.WriteLine('<script language="JScript">');
			
			// サブプロセス側ロジックを出力する。
			wsf.WriteLine('var __tempFilePath = "";');
			wsf.WriteLine('var __fso = new ActiveXObject("Scripting.FileSystemObject");');
			wsf.WriteLine('var postMessage = ' + __hta_worker_subprocess__postMessage.toString());
			wsf.WriteLine(__hta_worker_subprocess__main.toString());
			wsf.WriteLine(__hta_worker_subprocess__WriteLine.toString());
			wsf.WriteLine('__hta_worker_subprocess__main();');
			
			wsf.WriteLine('</script>');
			wsf.WriteLine('</job>');
			wsf.Close();
		}
		
		this.wcExec = this.wsh.Exec('wscript //nologo "' + this.workerThreadScriptPath + '" "' + this.tempFilePath + '"');
		var paramObject = {data: param};
		var jsonParamText = JSON.stringify(paramObject);
		this.wcExec.StdIn.WriteLine(jsonParamText);
		
		if (typeof window != "undefined") {
			while (window[this.workerName] !== undefined) {
				this.workerName = "__worker_" + Math.floor(Math.random() * 1000000)
			}
			window[this.workerName] = this;
			setTimeout("IntervalMessageLoop(window['" + this.workerName + "'])",10);
			if (! window.attached_deleteAllWorkers) {
				window.attachEvent("onbeforeunload", deleteAllWorkers);
				window.attached_deleteAllWorkers = true;
			}
		}
	}
	this.InvokeOnMessage = function(dataObject) {
		this.onmessage({data: dataObject});
	}
	this.finalize = function() {
		if (this.wcExec.Status == 0) {
			// まだ起動しているので、終了させる
			try {
				this.wcExec.Terminate();
			} catch (e) {
				log("Exception at worker.wcExec.Terminate(): " + e);
			}
			log("wcExec.Terminate()! Status: " + this.wcExec.Status);
		}
		if (window) {
			window.detachEvent("onbeforeunload", this.finalize);
			if (window[this.workerName]) {
				window[this.workerName] = undefined;
			}
			log("after delete! window['" + this.workerName + "'] " + window[this.workerName]);
		}
		if (this.fso.FileExists(this.tempFilePath)) {
			this.fso.DeleteFile(this.tempFilePath);
		}
		if (this.fso.FileExists(this.workerThreadScriptPath)) {
			this.fso.DeleteFile(this.workerThreadScriptPath);
		}
	}
}

function DoMessageLoop(worker) {
	while (! worker.wcExec.StdOut.AtEndOfStream) {
		var messageText = worker.wcExec.StdOut.ReadLine();
		var messageObject = JSON.parse(messageText);
		worker.InvokeOnMessage(messageObject);
	}
}

function ReadLineNonblocking(ts, size) {
	log("★begin - ReadLineNonblocking()");
	var s = "";
	var read = 0;
	while (read < size) {
		s += ts.Read(1);
		if (s.substr(s.length - 1, 1) == "\n") {
			break;
		}
	}
	log("★end - ReadLineNonblocking(): '" + s + "'");
	return s.substr(0, s.length - 2);
}

function IntervalMessageLoop(worker) {
	if (true) {
		var fileSize = 0;
		if (worker.fso.FileExists(worker.tempFilePath)) {
			fileSize = worker.fso.GetFile(worker.tempFilePath).Size;
		}
		var bufferedCharCount = (fileSize - 2) / 2;
		if (bufferedCharCount < 0) bufferedCharCount = 0;
		var interval = 100;
		if (worker.readCharCount < bufferedCharCount) {
			var messageText = ReadLineNonblocking(worker.wcExec.StdOut, bufferedCharCount - worker.readCharCount);
			var messageObject = JSON.parse(messageText);
			worker.readCharCount += (messageText.length + 2);
			worker.InvokeOnMessage(messageObject);
			if (worker.readCharCount == bufferedCharCount) {
				interval = 0;
			} else {
				interval = 10;
			}
		}
		var terminated = false;
		if (worker.readCharCount == bufferedCharCount) {
			if (worker.wcExec.Status == 1) {
				terminated = true;
			}
		}
		if (terminated) {
			worker.finalize();
		} else {
			// 「対象アプリケーションが終了していない」or「出力を読み切っていない」場合にsetTimeout()
			window.setTimeout("IntervalMessageLoop(window['" + worker.workerName + "'])", interval);
		}
	}
}

var __wwhost_logfilename;
var __fso_logfile;
function log(s) {
	if (! __wwhost_logfilename) {
		var TemporaryFolder = 2;
		var fso = new ActiveXObject("Scripting.FileSystemObject");
		var logfname = fso.GetSpecialFolder(TemporaryFolder) + "\\hta_worker_" + formatDate(new Date()) + ".log";
		__wwhost_logfilename = logfname;
	}
	var s2 = "[" + (new Date().toString()) + "] " + s;
	if (! __fso_logfile) {
		__fso_logfile = new ActiveXObject("Scripting.FileSystemObject");
	}
	var fso = __fso_logfile;
	var ForAppending = 8;
	var TristateTrue = -1;
	var ts = fso.OpenTextFile(__wwhost_logfilename, ForAppending, true, TristateTrue);
	ts.WriteLine(s2);
	ts.Close();
}

/// 以下、サブプロセス側WSHで動作するスクリプト

//! サブプロセス側で定義すべき変数
/// var __tempFilePath = "";
/// var __fso = new ActiveXObject("Scripting.FileSystemObject");

// サブプロセス側 postMessage()
var __hta_worker_subprocess__postMessage = function(param) {
	var jsonText = JSON.stringify(param);
	__hta_worker_subprocess__WriteLine(jsonText);
}

// サブプロセス側main()
function __hta_worker_subprocess__main() {
	var inputText = WScript.StdIn.ReadLine();
	var paramObject = JSON.parse(inputText);
	
	var args = WScript.Arguments;
	if (args.Count() >= 1) {
		__tempFilePath = args(0);
	}
	onmessage(paramObject);
}

// サブプロセス側 出力関数
function __hta_worker_subprocess__WriteLine(s) {
	WScript.StdOut.WriteLine(s);
	if (__tempFilePath) {
		var ForAppending = 8;
		var TristateTrue = -1;
		var ts = __fso.OpenTextFile(__tempFilePath, ForAppending, true, TristateTrue);
		ts.WriteLine(s);
		ts.Close();
	}
}
